using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using AiPlatformAdmin.Api.Data;
using AiPlatformAdmin.Api.Services;
using AiPlatformAdmin.Api.Middleware;
using Microsoft.KernelMemory;
using AiPlatformAdmin.Api.Mapping;

var builder = WebApplication.CreateBuilder(args);

// Register services
builder.Services.AddScoped<ITenantService, TenantService>();
builder.Services.AddScoped<DatabaseInitializer>();
builder.Services.AddAutoMapper(typeof(AutoMapperProfile).Assembly);

// Configure KernelMemory (optional - only if API key is provided)
var kernelMemoryConfig = builder.Configuration.GetSection("KernelMemory");
var openAiApiKey = kernelMemoryConfig["OpenAI:ApiKey"] ?? Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? "";
var vectorDbPath = kernelMemoryConfig["VectorDb:Directory"] ?? "./vectordb";

if (!string.IsNullOrEmpty(openAiApiKey))
{
    // Only initialize KernelMemory if API key is available
    var memory = new KernelMemoryBuilder()
        .WithOpenAI(new OpenAIConfig
        {
            APIKey = openAiApiKey,
            EmbeddingModel = "text-embedding-ada-002",
            TextModel = "gpt-4"
        })
        .WithSimpleVectorDb(vectorDbPath)
        .Build<MemoryServerless>();

    builder.Services.AddSingleton<IKernelMemory>(memory);
}
else
{
    // Log warning that KernelMemory is not configured
    Console.WriteLine("WARNING: OpenAI API key not configured. Document vectorization will not be available.");
    Console.WriteLine("Set OPENAI_API_KEY environment variable or configure KernelMemory:OpenAI:ApiKey in appsettings.json");
}

// Always register DocumentProcessingService (it will work without KernelMemory, just without vectorization)
builder.Services.AddScoped<DocumentProcessingService>();

// Add DbContext with PostgreSQL
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")
        ?? "Host=localhost;Database=aiplatform;Username=postgres;Password=postgres"));

// Add CORS
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowFrontend", policy =>
    {
        // For local development, be permissive to avoid CORS issues. Use a stricter policy in production.
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

// Add JWT Authentication
var jwtSettings = builder.Configuration.GetSection("JwtSettings");
var secretKey = jwtSettings["SecretKey"] ?? "YourSuperSecretKeyForJWTAuthenticationMinimum32Characters";

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = jwtSettings["Issuer"] ?? "AiPlatformAdminApi",
        ValidAudience = jwtSettings["Audience"] ?? "AiPlatformAdminClient",
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
    };
});

builder.Services.AddAuthorization();

// Add Controllers
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;
    });

// Add Swagger/OpenAPI
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
    {
        Title = "AI Platform Admin API",
        Version = "v1",
        Description = "API for managing AI Platform Admin system including users, teams, virtual experts, knowledge bases, and plugins"
    });

    // Add JWT authentication to Swagger
    options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below.",
        Name = "Authorization",
        In = Microsoft.OpenApi.Models.ParameterLocation.Header,
        Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });

    options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
    {
        {
            new Microsoft.OpenApi.Models.OpenApiSecurityScheme
            {
                Reference = new Microsoft.OpenApi.Models.OpenApiReference
                {
                    Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            Array.Empty<string>()
        }
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline
// Top-level middleware: always run first to log requests and ensure CORS headers are present (dev troubleshooting)
app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILoggerFactory>().CreateLogger("TopCorsMiddleware");
    var origin = context.Request.Headers["Origin"].ToString();
    logger.LogInformation("TopCorsMiddleware: {Method} {Path} Origin={Origin}", context.Request.Method, context.Request.Path, origin);

    // Add permissive CORS headers for debugging (remove or tighten in production)
    context.Response.Headers["Access-Control-Allow-Origin"] = string.IsNullOrEmpty(origin) ? "*" : origin;
    context.Response.Headers["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE,OPTIONS";
    context.Response.Headers["Access-Control-Allow-Headers"] = "Content-Type,Authorization";
    context.Response.Headers["X-Top-Cors"] = "true";

    if (string.Equals(context.Request.Method, "OPTIONS", StringComparison.OrdinalIgnoreCase))
    {
        context.Response.StatusCode = StatusCodes.Status200OK;
        await context.Response.WriteAsync(string.Empty);
        return;
    }

    await next();
});

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "AI Platform Admin API v1");
    });
}

// Ensure routing runs before CORS so the middleware can apply to endpoints
app.UseRouting();

// Apply CORS before HTTPS redirection and authentication so preflight requests are handled correctly
app.UseCors("AllowFrontend");

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseTenantMiddleware();
app.UseAuthorization();

app.MapControllers();

// Initialize database on startup
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var logger = services.GetRequiredService<ILogger<Program>>();

    try
    {
        var autoMigrate = builder.Configuration.GetValue<bool>("Database:AutoMigrate", true);
        var initializeDb = builder.Configuration.GetValue<bool>("Database:AutoInitialize", true);

        if (autoMigrate)
        {
            logger.LogInformation("Applying database migrations...");
            var dbContext = services.GetRequiredService<AppDbContext>();
            await dbContext.Database.MigrateAsync();
            logger.LogInformation("Database migrations applied successfully.");
        }
        else
        {
            logger.LogInformation("Database auto-migration is disabled. Skipping migrations.");
        }

        if (initializeDb)
        {
            logger.LogInformation("Seeding initial data...");
            var initializer = services.GetRequiredService<DatabaseInitializer>();
            await initializer.SeedDataAsync();
        }
        else
        {
            logger.LogInformation("Database seed data initialization is disabled. Skipping seed data.");
        }
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "An error occurred during database initialization.");
        throw;
    }
}

app.Run();
